#pragma once

#include "CameraArcball.hxx"
#include "CameraBase.hxx"

namespace Eifersucht
{
	class ModelViewerCamera : public BasicCamera
	{
#pragma region Hidden base members
	private:
		using BasicCamera::SetDragRect;
		using BasicCamera::_Basic_camera_view;
		using BasicCamera::_Basic_camera_input;
		using BasicCamera::_Basic_camera_reset;
		using BasicCamera::_Basic_camera_velocity;
		using BasicCamera::_Basic_camera_post_on_pointer_pressed;
		using BasicCamera::_Basic_camera_post_on_pointer_released;
		using BasicCamera::_Basic_camera_on_pointer_left_released;
		using BasicCamera::_Basic_camera_on_pointer_left_pressed;
		using BasicCamera::_Basic_camera_on_pointer_right_pressed;
		using BasicCamera::_Basic_camera_on_pointer_right_released;
		using BasicCamera::_Basic_camera_on_pointer_middle_pressed;
		using BasicCamera::_Basic_camera_on_pointer_middle_released;

#pragma endregion Hidden base members

#pragma region Owned members
	protected:

		Float32x4x4 m_f4x4CameraLastRotation;				// Last arc ball rotation matrix for camera
		Float32x4x4 m_f4x4ModelLastRotation;				// Last arc ball rotation matrix for model
		Float32x4x4 m_f4x4ModelRotation;					// Rotation matrix of the model
		Float32x4x4 m_f4x4World;							// World matrix of the model
		Float32x3 m_f3ModelCenter;							// Model center coords

		Int32 m_iRotateCameraButtonMask;					// Buttons to rotate the camera around the model
		Int32 m_iRotateModelButtonMask;						// Buttons to rotate the taget model
		Int32 m_iZoomModelButtonMask;						// Buttons to zoom camera to/from model

		Arcball m_lWorldArcball;							// Camera world arc ball
		Arcball m_lViewArcball;								// Camera view arc ball

		Float32 m_fDefaultRadius;							// Default distance from modelt to camera
		Float32 m_fMaxRadius;								// Max distance from modelt to camera
		Float32 m_fMinRadius;								// Min distance from modelt to camera
		Float32 m_fRadius;									// Distance from modelt to camera

		Bool m_bAttachCameraToModel;						// Indicates whether the camera is attached to model
		Bool m_bDragSinceLastUpdate;						// Indicates the pointer drag has happend since the last time OnFrameMove was called.
		Bool m_bLimitPitch;									// Indicates whether the pitch is limited.

#pragma endregion Owned members

#pragma region Properties
	public:

		inline Float32x4x4ConstPtr GetWorld() { return &m_f4x4World; }

		inline Void SetLimitPitch( _In_ Bool bLimit ) { m_bLimitPitch = bLimit; }
		inline Void SetModelCenter( _In_ Float32x3Ptr pf3Center	) { m_f3ModelCenter = (*pf3Center); }
		inline Void SetAttachCameraToModel( _In_ Bool bEnable = true ) { m_bAttachCameraToModel = bEnable; }
		inline Void SetWorldQuat( _In_ Quaternion quatf4World ) { m_lWorldArcball.SetNowQuat( quatf4World ); m_bDragSinceLastUpdate = true; }
		inline Void SetViewQuat( _In_ Quaternion quatf4View ) { m_lViewArcball.SetNowQuat( quatf4View ); m_bDragSinceLastUpdate = true; }
		inline Void SetWorld( Float32x4x4ConstRef rf4x4World ) { m_f4x4World = rf4x4World; m_bDragSinceLastUpdate = true; }

		inline Void SetButtons( 
			_In_ Int32 iRotateModelButton = _To_int PointerButton::Right, 
			_In_ Int32 iRotateCameraButton = _To_int PointerButton::Left
			)
		{
			m_iZoomModelButtonMask = _To_int PointerButton::Wheel;
			m_iRotateCameraButtonMask = iRotateCameraButton;
			m_iRotateModelButtonMask = iRotateModelButton;
		}

		inline Void SetWindow( 
			_In_ Int32 iWidth, _In_ Int32 iHeight, 
			_In_ Float32 fArcballRadius = ArcballDefaultRadius 
			)
		{
			m_lWorldArcball.SetWindow( iWidth, iHeight, fArcballRadius );
			m_lViewArcball.SetWindow( iWidth, iHeight, fArcballRadius );
		}

		inline Void SetRadius( 
			_In_ Float32 fDefault = 5.0f, 
			_In_ Float32 fMin = 1.0f, 
			_In_ Float32 fMax = FLT_MAX 
			)
		{
			m_fDefaultRadius = fDefault;
			m_fRadius = fDefault;
			m_fMinRadius = fMin;
			m_fMaxRadius = fMax;
			m_bDragSinceLastUpdate = true;
		}

#pragma endregion Properties

#pragma region Shared functionality
	public:

		ModelViewerCamera();

		Void OnFrameMove( _In_ Float32 fElapsed );
		Void SetDragRect( _In_ RectConstRef l4Drag );
		Void SetView( _In_ Float32x3Ptr pf3Eye, _In_ Float32x3Ptr pf3LookAt );
		Void Reset();

		Void OnCaptureChanged();
		Void OnPointerWheelChanged( _In_ Int16 iWheelDelta );

		Void OnPointerMoved( _In_ Int16 iX, _In_ Int16 iY );
		Void OnPointerLeftButtonPressed( _In_ Int16 iX, _In_ Int16 iY );
		Void OnPointerRightButtonPressed( _In_ Int16 iX, _In_ Int16 iY );
		Void OnPointerMiddleButtonPressed( _In_ Int16 iX, _In_ Int16 iY );

		Void OnPointerLeftButtonReleased();
		Void OnPointerRightButtonReleased();
		Void OnPointerMiddleButtonReleased();

#pragma endregion

	};
};